import 'package:flutter/material.dart';
import 'package:markdown_widget/markdown_widget.dart';
import 'package:markdown/markdown.dart' as m;
import 'package:flutter_math_fork/flutter_math.dart';

class MarkdownBody extends StatelessWidget {
  final String text;
  Function tap;
  bool isLight;
  bool? isApp;
  MarkdownBody({
    super.key,
    required this.text,
    required this.tap,
    required this.isLight,
    this.isApp,
  });

  @override
  Widget build(BuildContext context) {
    final isDark = Theme.of(context).brightness == Brightness.dark;
    final config = MarkdownConfig.defaultConfig;
    codeWrapper(child, text) => CodeWrapperWidget(child: child, text: text);
    return MarkdownWidget(
      shrinkWrap: true,
      data: text,
      padding: EdgeInsets.only(bottom: 0),
      markdownGenerator: MarkdownGenerator(
        inlineSyntaxList: [LatexSyntax()],
        generators: [latexGenerator],
      ),
      config: MarkdownConfig.defaultConfig.copy(
        configs: [
          ListConfig(
            marginLeft: 18,
          ),
          PConfig(
            textStyle: TextStyle(
              fontSize: 15,
              height: 1.8,
            ),
          ),
          PreConfig(
            wrapper: (child, code, language) {
              return Opacity(
                opacity: 1,
                child: child,
              );
            },
          ),
          TableConfig(
            wrapper: (table) => SingleChildScrollView(
              scrollDirection: Axis.horizontal,
              child: table,
            ),
          ),
        ],
      ),
    );
  }
}

class CodeWrapperWidget extends StatefulWidget {
  final Widget child;
  final String text;

  const CodeWrapperWidget({Key? key, required this.child, required this.text})
      : super(key: key);

  @override
  State<CodeWrapperWidget> createState() => _CodeWrapperWidgetState();
}

class _CodeWrapperWidgetState extends State<CodeWrapperWidget> {
  @override
  Widget build(BuildContext context) {
    return widget.child;
  }
}

SpanNodeGeneratorWithTag latexGenerator = SpanNodeGeneratorWithTag(
  tag: _latexTag,
  generator: (e, config, visitor) => LatexNode(
    e.attributes,
    e.textContent,
    config,
  ),
);

const _latexTag = 'latex';

class LatexSyntax extends m.InlineSyntax {
  LatexSyntax() : super(r'\$\$([\s\S]+?)\$\$|\$([\s\S]+?)\$');

  @override
  bool onMatch(m.InlineParser parser, Match match) {
    final input = match.input;
    final matchValue = input.substring(match.start, match.end);
    String content = '';
    bool isInline = true;
    const blockSyntax = '\$\$';
    const inlineSyntax = '\$';
    if (matchValue.startsWith(blockSyntax) &&
        matchValue.endsWith(blockSyntax) &&
        (matchValue != blockSyntax)) {
      content = matchValue.substring(2, matchValue.length - 2);
      isInline = false;
    } else if (matchValue.startsWith(inlineSyntax) &&
        matchValue.endsWith(inlineSyntax) &&
        matchValue != inlineSyntax) {
      content = matchValue.substring(1, matchValue.length - 1);
    }
    m.Element el = m.Element.text(_latexTag, matchValue);
    el.attributes['content'] = content;
    el.attributes['isInline'] = '$isInline';
    parser.addNode(el);
    return true;
  }
}

class LatexNode extends SpanNode {
  final Map<String, String> attributes;
  final String textContent;
  final MarkdownConfig config;

  LatexNode(this.attributes, this.textContent, this.config);

  @override
  InlineSpan build() {
    final content = attributes['content'] ?? '';
    final isInline = attributes['isInline'] == 'true';
    final style = parentStyle ?? config.p.textStyle;
    if (content.isEmpty) return TextSpan(style: style, text: textContent);
    final latex = Math.tex(
      content,
      textStyle: TextStyle(
        fontSize: 16,
        fontWeight: FontWeight.normal,
      ),
      mathStyle: MathStyle.text,
      // textStyle: style.copyWith(color: isDark ? Colors.white : Colors.black),
      textScaleFactor: 1,
      onErrorFallback: (error) {
        // print(error.message);
        print(content);
        return Text(
          textContent,
          style: style.copyWith(color: Colors.red),
        );
      },
    );
    return WidgetSpan(
      child: !isInline
          ? Container(
              width: double.infinity,
              margin: const EdgeInsets.symmetric(vertical: 16),
              child: Center(child: latex),
            )
          : latex,
    );
  }
}
